home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Communication / GatorGeo / Source / GeoClient.m < prev    next >
Text File  |  1992-03-09  |  15KB  |  464 lines

  1. // GeoClient.m
  2. // By Charles G. Fleming, Educational Computing Services, Allegheny College.
  3. // You may freely copy, distribute and reuse this code. 
  4. // Allegheny College and the author disclaim any warranty of any kind, 
  5. // expressed or implied, as to its fitness for any particular use.
  6. // This work was partially supported by a grant from the Vira Heinz Endowment.
  7.  
  8.  
  9. #import "GeoClient.h"
  10. #import "Subprocess.h"
  11.  
  12. #import <appkit/Panel.h>
  13. #import <appkit/Application.h>
  14. #import <appkit/TextField.h>
  15. #import <appkit/PopUpList.h>
  16. #import <appkit/MenuCell.h>
  17. #import <appkit/ScrollView.h>
  18. #import <appkit/Text.h>
  19. #import <appkit/Button.h>
  20.  
  21. #import <objc/Storage.h>
  22. #import <objc/Object.h>
  23.  
  24. #import <strings.h>
  25. #import <string.h>
  26. #import <stdlib.h>
  27.  
  28. @implementation GeoClient
  29.  
  30. int stuffer(struct dataRecord* , char *);
  31.     
  32. - appDidInit:sender
  33. {      
  34.     subprocess = [[Subprocess allocFromZone:[self zone]] init:"/bin/csh"  withDelegate:self                                         andPtySupport:YES andStdErr:YES]; 
  35.     [subprocess send:"telnet martini.eecs.umich.edu 3000"];
  36.  
  37.     records = [[Storage allocFromZone:[self zone]] initCount:10 
  38.          elementSize:sizeof(struct dataRecord)
  39.          description:"{[50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c]}"];
  40.  
  41.     // Set up the pull down lists.
  42.     statesPullDownList = [statesPullDownListButton target];
  43.     [statesPullDownList setTarget:self];
  44.     [statesPullDownList setAction:@selector(selectState:)];  
  45.     moreStatesPullDownList = [moreStatesPullDownListButton target];
  46.     [moreStatesPullDownList setTarget:self];
  47.     [moreStatesPullDownList setAction:@selector(selectState:)];  
  48.  
  49.     // Display a message until login is complete.
  50.     [NXApp runModalFor:loginMessageWindow];      
  51.     return self;
  52. }
  53.  
  54. // Query the database for information on a particular city.    
  55. - query:sender
  56. {
  57.     char *cityAndState;
  58.  
  59.     // Enable the button only after the query is finished.
  60.     [sender setEnabled:NO];           
  61.     
  62.     // So that we will receive text did change messages from our text fields.
  63.    [self makeFirstResponder:self];
  64.  
  65.     // Reset the pull down list, clear the data records in the storage object and clear the
  66.     // text fields.
  67.     [self resetPullDownList];
  68.     [records empty];
  69.     [self clearTextFields];
  70.     
  71.     cityAndState = malloc(strlen([cityTextField stringValue])+strlen([stateTextField stringValue])+3);
  72.     
  73.     // Build the query string for the database server.  The query string will be  "City, State"  or "City".
  74.     if(strlen([stateTextField stringValue]) > 0)
  75.     sprintf(cityAndState,  "%s, %s", [cityTextField stringValue],[stateTextField stringValue]);
  76.     else
  77.         strcpy(cityAndState, [cityTextField stringValue]);
  78.     
  79.     // Send the query to the subprocess object.       
  80.     [subprocess send:cityAndState];
  81.  
  82.     // Searching message window.
  83.     [searchingMessageWindow makeKeyAndOrderFront:self];
  84.     free(cityAndState);      
  85.     return self;
  86. }
  87.  
  88. // Put the information for the state selected from the pull down list into the text fields.
  89. - selectState:sender
  90. {
  91.     int tag;
  92.     struct dataRecord *newRecord;
  93.  
  94.     // Get the record for the selected state.   
  95.     tag = [[sender selectedCell] tag];
  96.     newRecord = (struct dataRecord *)[records elementAt:tag];
  97.  
  98.      // Put the information into the text fields.
  99.     [stateTextField setStringValue:newRecord->stateName];    
  100.     [countyCodeTextField setStringValue:newRecord->countyCode];
  101.     [countyNameTextField setStringValue:newRecord->countyName];
  102.     [stateAbbrTextField setStringValue:newRecord->stateAbbr];
  103.     [stateNameTextField setStringValue:newRecord->stateName];
  104.     [nationAbbrTextField setStringValue:newRecord->nationAbbr];
  105.     [nationNameTextField setStringValue:newRecord->nationName];
  106.     [areaCodeTextField setStringValue:newRecord->areaCode];
  107.     [elevationTextField setStringValue:newRecord->elevation];
  108.     [featureCodeTextField setStringValue:newRecord->featureCode];
  109.     [featureNameTextField setStringValue:newRecord->featureName];
  110.     [latitudeTextField setStringValue:newRecord->latitude];
  111.     [longitudeNameTextField setStringValue:newRecord->longitude];
  112.     [censusTextField setStringValue:newRecord->census];
  113.     [remarkTextField setStringValue:newRecord->remark];
  114.     [timeZoneTextField setStringValue:newRecord->timeZone];
  115.     [[zipScrollView docView] setText:newRecord->zip];
  116.     return self;
  117. }
  118.  
  119. // Clear all of the text fields.
  120. - clearTextFields
  121. {
  122.     [countyCodeTextField setStringValue:""];
  123.     [countyNameTextField setStringValue:""];
  124.     [stateAbbrTextField setStringValue:""];
  125.     [stateNameTextField setStringValue:""];
  126.     [nationAbbrTextField setStringValue:""];
  127.     [nationNameTextField setStringValue:""];
  128.     [areaCodeTextField setStringValue:""];
  129.     [elevationTextField setStringValue:""];
  130.     [featureCodeTextField setStringValue:""];
  131.     [featureNameTextField setStringValue:""];
  132.     [latitudeTextField setStringValue:""];
  133.     [longitudeNameTextField setStringValue:""];
  134.     [censusTextField setStringValue:""];
  135.     [remarkTextField setStringValue:""];
  136.     [timeZoneTextField setStringValue:""];
  137.     [[zipScrollView docView] setText:""];
  138.     return self;
  139. }
  140.  
  141. // Remove the entries from the pull down lists.
  142. - resetPullDownList
  143. {
  144.     int statesCount, state;
  145.  
  146.     statesCount = [statesPullDownList count];
  147.     for(state = statesCount-1; state > 0; state--)
  148.         [statesPullDownList removeItemAt:state];
  149.  
  150.     statesCount = [moreStatesPullDownList count];
  151.     for(state = statesCount-1; state > 0; state--)
  152.         [moreStatesPullDownList removeItemAt:state];
  153.     return self;
  154. }  
  155.             
  156. - textDidChange:textObject
  157. {
  158.     TextField *textField;
  159.     
  160.     // Clear the text fields.
  161.     [self clearTextFields];
  162.     
  163.     // Reset the pull down list.
  164.     [self resetPullDownList];    
  165.     [statesPullDownListButton setEnabled:NO];         
  166.     [moreStatesPullDownListButton setEnabled:NO];         
  167.     
  168.     // Emtpy all city records.    
  169.     [records empty];
  170.     
  171.     textField = [textObject delegate];    
  172.     if(textField == cityTextField)
  173.         [stateTextField setStringValue:""];    
  174.     return self;
  175. }
  176.     
  177. - appWillTerminate:sender
  178. {
  179.     [subprocess send:"bye"];
  180.     [subprocess terminate:self];
  181.     return self;
  182. }
  183.  
  184. // Process the output from the subprocess object. 
  185. - subprocessOutput:(char *)buffer
  186. {  
  187.     static char *message;
  188.     char *period, *start, *end;
  189.     static BOOL firstTime = YES;
  190.     static BOOL login = YES;
  191.     int cityCount;
  192.     MenuCell *menuCell;
  193.  
  194.     // Malloc a message buffer only once.
  195.     if(firstTime)
  196.     {
  197.         message = malloc(10000);
  198.     firstTime = NO;  
  199.      }  
  200.  
  201.     // Process a login.       
  202.     strcat(message, buffer);
  203.     if(login)
  204.     {    
  205.         period = rindex(message, '.');
  206.         if(period )
  207.             if(*(period - 1) == '\n')
  208.             {  
  209.             [loginMessageWindow close];
  210.             [NXApp stopModal];
  211.         [queryButton setEnabled:YES];     
  212.                 bzero(message, strlen(message)+1);  
  213.                 *(message+1) = '\0';    
  214.         login = NO;     
  215.             }    
  216.     }    
  217.     // Process the data returned from the database server.
  218.     else
  219.     {
  220.         // Process the records only after all buffers have arrived.
  221.         if(strstr(message, "\n."))
  222.     {  
  223.         // No cities found.
  224.         if(!strstr(message, "\n0"))
  225.         {
  226.                 bzero(message, strlen(message)+1);  
  227.         [searchingMessageWindow close];
  228.         [queryButton setEnabled:YES];     
  229.         }  
  230.         // Process the cities.
  231.             else
  232.             {  
  233.                 // Process all records but the last one (states).
  234.             cityCount = 0;
  235.                 start = strstr(message, "\n0")+1;
  236.             while(end = strstr(start, "\n0"))
  237.             {
  238.                 *end = '\0';
  239.                 // Clear out the record and then stuff all of the information into it.
  240.             bzero(&record, sizeof(struct dataRecord));
  241.             
  242.             if(stuffer(&record, start))
  243.             {    
  244.                 if(cityCount < 39)
  245.             {
  246.                         // Check for duplicate states.  The data base has some duplicates.
  247.                             while( [statesPullDownList indexOfItem:record.stateName]  != -1)
  248.                             strcat(record.stateName, " ");
  249.  
  250.                         // Get the state and add a button to the pulldown list.
  251.                          menuCell = [statesPullDownList addItem:record.stateName];
  252.                      [menuCell setTag:cityCount];  
  253.             }
  254.             else
  255.             {
  256.                         // Check for duplicate states.  The data base has some duplicates.
  257.                             while( [moreStatesPullDownList indexOfItem:record.stateName]  != -1)
  258.                             strcat(record.stateName, " ");
  259.  
  260.                         // Get the state and add a button to the pulldown list.
  261.                          menuCell = [moreStatesPullDownList addItem:record.stateName];
  262.                      [menuCell setTag:cityCount];  
  263.             }
  264.                     [records addElement:&record];  
  265.                     cityCount++;
  266.             }        
  267.                 start = end+1;
  268.             }
  269.     
  270.             // Now process the last record (state).
  271.             end = strstr(start, "\n.");
  272.             *end = '\0';
  273.     
  274.             // Clear out the record and then stuff all of the information into it.
  275.         bzero(&record, sizeof(struct dataRecord));            
  276.         if(stuffer(&record, start))
  277.         {
  278.             if(cityCount < 39)
  279.             {
  280.                     // Check for duplicate states.  The data base has some duplicates.
  281.                         while( [statesPullDownList indexOfItem:record.stateName]  != -1)
  282.                        strcat(record.stateName, " ");
  283.  
  284.                     // Get the state and add a button to the pulldown list.
  285.                      menuCell = [statesPullDownList addItem:record.stateName];
  286.                  [menuCell setTag:cityCount];  
  287.             }
  288.             else
  289.             {
  290.                     // Check for duplicate states.  The data base has some duplicates.
  291.                         while( [moreStatesPullDownList indexOfItem:record.stateName]  != -1)
  292.                         strcat(record.stateName, " ");
  293.  
  294.                     // Get the state and add a button to the pulldown list.
  295.                      menuCell = [moreStatesPullDownList addItem:record.stateName];
  296.                  [menuCell setTag:cityCount];  
  297.             }
  298.                 [records addElement:&record];  
  299.                 cityCount++;
  300.         }
  301.         if(cityCount > 0)    
  302.             [statesPullDownListButton setEnabled:YES];  
  303.         else
  304.             [statesPullDownListButton setEnabled:NO];  
  305.         if(cityCount > 38)
  306.             [moreStatesPullDownListButton setEnabled:YES];             
  307.         else
  308.             [moreStatesPullDownListButton setEnabled:NO];  
  309.                 bzero(message, strlen(message)+1);  
  310.         [searchingMessageWindow close];
  311.                 [queryButton setEnabled:YES];        
  312.             }
  313.         }      
  314.     }       
  315.     return self;
  316. }
  317.     
  318. // This is sent when the connection is broken, or when we quit the application.
  319. - subprocessDone
  320. {
  321.     NXRunAlertPanel("BYE", "You are no longer connected to the remote machine.", 
  322.                             NULL, NULL, NULL);
  323.     return self;
  324. }
  325.  
  326. // Display any error messages received.
  327. - subprocessError:(const char *)errorString
  328. {
  329.     [errorMessageWindow makeKeyAndOrderFront:self];
  330.     [[errorMessageScrollView docView] setText:errorString];
  331.     return self;
  332. }
  333.  
  334. // Takes each string, corresponding to a city and specific state,  and puts the information 
  335. // into a record.
  336. int stuffer(struct dataRecord *record , char *entry)
  337. {
  338.     char *zero, *start, *copy, *linefeed, *string, *nextSpace; 
  339.     BOOL stateFound = NO, done = NO;
  340.     int firstChar, blank;
  341.  
  342.     copy = malloc(strlen(entry) + 1);
  343.     strcpy(copy, entry);
  344.  
  345.     // Find the city field and put it into the structure.       
  346.     zero = index(copy, '0')+2;
  347.     linefeed = index(zero, '\n');
  348.     *(linefeed) = '\0';
  349.     strcpy(record->city, zero);
  350.  
  351.     // Get the fields and put them into the structure.
  352.     start =     linefeed +1;
  353.     while(!done)
  354.     {
  355.         // Put a null terminator at the end of each line before processing the line.
  356.         linefeed = index(start, '\n');
  357.       *linefeed = '\0';
  358.     
  359.         // Based on the first character, put the field into the structure.
  360.         firstChar = (int)*(start);
  361.         
  362.         switch(firstChar)
  363.     {        
  364.             case '1':
  365.             // Get the county code.
  366.                 string = index(start, ' ')+1;
  367.             nextSpace = index(string, ' ');
  368.             *nextSpace = '\0';
  369.                     strcpy(record->countyCode, string);
  370.  
  371.             // Get the county name.      
  372.                 string = nextSpace+1;
  373.                     strcpy(record->countyName, string);    
  374.             break;                
  375.             case '2':
  376.             stateFound = YES;
  377.             // Get the state abbreviation.
  378.                 string = index(start, ' ')+1;
  379.             nextSpace = index(string, ' ');
  380.             *nextSpace = '\0';
  381.                     strcpy(record->stateAbbr, string);
  382.             
  383.             // Get the state name.      
  384.                 string = nextSpace+1;
  385.                     strcpy(record->stateName, string);
  386.                     break;  
  387.             case '3':
  388.             // Get the nation abbreviation.
  389.                 string = index(start, ' ')+1;
  390.             nextSpace = index(string, ' ');
  391.             *nextSpace = '\0';
  392.                     strcpy(record->nationAbbr, string);
  393.             
  394.             // Get the nation name.      
  395.                 string = nextSpace+1;
  396.                     strcpy(record->nationName, string);
  397.                     break;  
  398.             case 'A':
  399.             // Get the area code.
  400.                 string = index(start, ' ')+1;
  401.                     strcpy(record->areaCode, string);
  402.                     break;  
  403.             case 'E':
  404.             // Get the elevation.
  405.                 string = index(start, ' ')+1;
  406.                     strcpy(record->elevation, string);
  407.                     break;  
  408.             case 'F':
  409.             // Get the feature code.
  410.                 string = index(start, ' ')+1;
  411.             nextSpace = index(string, ' ');
  412.             *nextSpace = '\0';
  413.                     strcpy(record->featureCode, string);
  414.             
  415.             // Get the feature name.      
  416.                 string = nextSpace+1;
  417.                     strcpy(record->featureName, string);
  418.                     break;  
  419.             case 'L':                   
  420.             // Get the latitude.
  421.                 string = index(start, ' ')+1;
  422.             nextSpace = index(string, ' ');    
  423.             for(blank = 0; blank < 3; blank++)
  424.                 nextSpace = index(nextSpace+1, ' ');
  425.             *nextSpace = '\0';
  426.                     strcpy(record->latitude, string);
  427.  
  428.             // Get the longitude.      
  429.                 string = nextSpace+1;
  430.                     strcpy(record->longitude, string);        
  431.             break;  
  432.             case 'P':
  433.             // Get the census.
  434.                 string = index(start, ' ')+1;
  435.                     strcpy(record->census, string);
  436.                     break;  
  437.             case 'R':
  438.             // Get the remark.
  439.                 string = index(start, ' ')+1;
  440.                     strcpy(record->remark, string);
  441.                     break;           
  442.             case 'T':
  443.             // Get the time zone.
  444.                 string = index(start, ' ')+1;
  445.                     strcpy(record->timeZone, string);
  446.                     break;  
  447.             case 'Z':                    
  448.             // Get the zip codes.
  449.                 string = index(start, ' ')+1;
  450.                     strcat(record->zip, string);
  451.                     break;                
  452.     }
  453.  
  454.     if(index(linefeed+1, '\n'))
  455.         start = linefeed + 1;
  456.     else
  457.         done = YES;    
  458.     }
  459.     free(copy); 
  460.     return stateFound;
  461. }    
  462.     
  463. @end
  464.